package serverMgr

import (
	"encoding/json"
	"fmt"
	"path"
	"src/gcommon"
	"src/zeus/etcd"
	"src/zeus/iserver"
	"src/zeus/logger"
	"sync"

	"github.com/buguang01/util"
	"go.etcd.io/etcd/api/v3/mvccpb"
	clientv3 "go.etcd.io/etcd/client/v3"
)

/* 某个类型的所有服务器信息(一般是只读) */
type ServerMapByType struct {
	lock *sync.RWMutex
	//服务器信息
	dli   map[uint64]map[uint64]*iserver.ServerInfo // key=serverType<<32+groupid, value=map[serverId]serverinfo: 类型+组ID -> 服务器ID -> 服务器信息
	arrli []*iserver.ServerInfo                     // 服务器列表 (按照加入顺序)
}

func NewServerMapByType() (result *ServerMapByType) {
	result = new(ServerMapByType)
	result.lock = new(sync.RWMutex)
	result.dli = make(map[uint64]map[uint64]*iserver.ServerInfo)
	result.arrli = make([]*iserver.ServerInfo, 0, 1)
	return
}

// 加入服务器信息
func (this *ServerMapByType) Put(sinfo *iserver.ServerInfo) {
	this.lock.Lock()
	defer this.lock.Unlock()
	key := uint64(sinfo.Type)<<32 + uint64(sinfo.Group)
	if li, ok := this.dli[key]; !ok {
		li = make(map[uint64]*iserver.ServerInfo)
		this.dli[key] = li
		li[sinfo.ServerID] = sinfo
	} else {
		li[sinfo.ServerID] = sinfo
	}
	for i, md := range this.arrli {
		if md.ServerID == sinfo.ServerID {
			this.arrli[i] = sinfo
			return
		}
	}
	this.arrli = append(this.arrli, sinfo)

}

// 删除一个服务器信息
func (this *ServerMapByType) Delete(path string) {
	groupid, stype, serverid := GetPathServerInfo(path)
	this.lock.Lock()
	defer this.lock.Unlock()
	key := uint64(stype)<<32 + uint64(groupid)
	if li, ok := this.dli[key]; ok {
		delete(li, serverid)
	}
	for i, md := range this.arrli {
		if md.ServerID == serverid {
			this.arrli = append(this.arrli[0:i], this.arrli[i+1:]...)
			return
		}
	}
}

// 更新Load值
func (this *ServerMapByType) UpLoad(path string, val int64) *iserver.ServerInfo {
	groupid, stype, sid := GetPathServerInfo(path)
	this.lock.Lock()
	defer this.lock.Unlock()
	key := uint64(stype)<<32 + uint64(groupid)
	if li, ok := this.dli[key]; ok {
		if sinfo, ok := li[sid]; ok {
			sinfo.Load = val
			return sinfo
		}
	}
	return nil
}

// 根据负载逻辑，获取服务器
func (this *ServerMapByType) GetBalancing(groupid uint32, stype uint8) (result *iserver.ServerInfo, err error) {
	this.lock.RLock()
	defer this.lock.RUnlock()
	result = nil
	key := uint64(stype)<<32 + uint64(groupid)
	srvlen := 0
	if li, ok := this.dli[key]; ok {
		srvlen = len(li)
		for _, v := range li {
			if v.Status == iserver.SERVER_STATUS_FORBID {
				continue
			}
			if result == nil {
				result = v
			} else if v.Load < 80 {
				return v, nil
			} else if result.Load > v.Load {
				result = v
			}
		}
	}
	if result == nil {
		return result, fmt.Errorf("Server list is empty, Type %d SrvLen: %d ", stype, srvlen)
	}
	return
}

// 返回所有的服务器信息<需要根据服务器类型使用 且是本服服务器>
func (this *ServerMapByType) GetServerList(groupid uint32, stype uint8) (result []iserver.ServerInfo, err error) {
	this.lock.RLock()
	defer this.lock.RUnlock()
	result = make([]iserver.ServerInfo, len(this.arrli))
	for i := range this.arrli {
		result[i] = *this.arrli[i]
	}
	return result, err
	// key := uint64(stype)<<32 + uint64(groupid)
	// if li, ok := this.dli[key]; ok {
	// 	result = make([]*iserver.ServerInfo, len(li))
	// 	i := 0
	// 	for _, v := range li {
	// 		result[i] = v
	// 		i++
	// 	}
	// }
}

// 获取指定服务器
func (this *ServerMapByType) GetServerByID(groupid uint32, stype uint8, serverId uint64) (result *iserver.ServerInfo, err error) {
	this.lock.RLock()
	defer this.lock.RUnlock()
	key := uint64(stype)<<32 + uint64(groupid)
	if li, ok := this.dli[key]; ok {
		if sinfo, ok := li[serverId]; ok {
			return sinfo, nil
		}
	}
	return nil, fmt.Errorf("Cant get server, Type %d  Id %d not existed", stype, serverId)
}

// 删除所有服务器信息
func (this *ServerMapByType) DeleteAll() {
	this.lock.RLock()
	defer this.lock.RUnlock()
	this.dli = make(map[uint64]map[uint64]*iserver.ServerInfo)
	this.arrli = make([]*iserver.ServerInfo, 0, 1)
}

// 监听函数
func (this *ServerMapByType) WatchCallDelay(evt *clientv3.Event) {
	if evt.Type == mvccpb.PUT {
		//有服务器信息过来
		var sinfo *iserver.ServerInfo
		_, sidstr := path.Split(string(evt.Kv.Key))
		switch sidstr {
		case PATH_LOAD:
			//如果是状态更新
			val := util.NewString(string(evt.Kv.Value)).ToInt64V()
			sinfo = this.UpLoad(string(evt.Kv.Key), val)
		case PATH_INFO:
			//服务器主信息更新、
			sinfo = new(iserver.ServerInfo)
			if err := json.Unmarshal(evt.Kv.Value, sinfo); err != nil {
				//可能需要输出信息
				return
			}
			this.Put(sinfo)
			logger.Debug(logger.LogKey_Etcd, "WatchCallDelay put PATH_INFO:", string(evt.Kv.Key), sinfo)
		default:
			logger.Debug(logger.LogKey_Etcd, "WatchCallDelay put unknown:", string(evt.Kv.Key), sinfo)
		}
		if sinfo != nil && iserver.GetSrvInst() != nil && iserver.GetSrvInst().GetSrvType() != gcommon.ServerTypeLogin {
			//监听的服务器信息更新，判断是否需要连接
			iserver.GetSrvInst().TryConnectToSrv(sinfo)
		}
	} else if evt.Type == mvccpb.DELETE {
		_, sidstr := path.Split(string(evt.Kv.Key))
		if sidstr == PATH_INFO {
			//删一个服务器信息
			this.Delete(string(evt.Kv.Key))
			logger.Debug(logger.LogKey_Etcd, "WatchCallDelay delete PATH_INFO:", string(evt.Kv.Key))
		}
	}
}

func (this *ServerMapByType) LoadWatchAll(key string) {
	res, err := etcd.GetEtcd().GetPathAll(key)
	if err != nil {
		logger.Error("loadWatchAll:", err)
		return
	}
	logger.Debug(logger.LogKey_Etcd, "LoadWatchAll", key, len(res.Kvs))
	for _, kv := range res.Kvs {
		var sinfo *iserver.ServerInfo
		_, sidstr := path.Split(string(kv.Key))
		switch sidstr {
		case PATH_LOAD:
			//如果是状态更新
			val := util.NewString(string(kv.Value)).ToInt64V()
			sinfo = this.UpLoad(string(kv.Key), val)
			logger.Debug(logger.LogKey_Etcd2, "LoadWatchAll LoadInfo", key, sinfo)
		case PATH_UserNum:
			panic("UserNum not support here")
		case PATH_INFO:
			sinfo = new(iserver.ServerInfo)
			if err := json.Unmarshal(kv.Value, sinfo); err != nil {
				//可能需要输出信息
				continue
			}
			this.Put(sinfo)
			logger.Debug(logger.LogKey_Etcd, "LoadWatchAll PathInfo", key, sinfo)
		default:
			logger.Debug(logger.LogKey_Etcd, "WatchCallDelay put unknown:", string(kv.Key), sinfo)
		}
		if sinfo != nil && iserver.GetSrvInst() != nil && iserver.GetSrvInst().GetSrvType() != gcommon.ServerTypeLogin {
			//监听的服务器信息更新，判断是否需要连接
			iserver.GetSrvInst().TryConnectToSrv(sinfo)
		}
	}
}
